package ar.com.arkios.kfcon4d.mapper;

import ar.com.arkios.kfcon4d.Connection4D;
import ar.com.arkios.kfcon4d.dao.GenericDAO;
import ar.com.arkios.kfcon4d.exception.Open4DException;
import ar.com.arkios.j4d.opConstants;
import ar.com.arkios.j4d.opData;
import ar.com.arkios.j4d.opDataArray;
import ar.com.arkios.j4d.opException;
import ar.com.arkios.j4d.opField;
import ar.com.arkios.j4d.opProcess;
import ar.com.arkios.j4d.opTable;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import org.apache.commons.beanutils.BeanUtils;

/**
 * <p>
 * Un Data Mapper debe administrar y encapsular toda la información 
 * correspondiente a la tabla de la entidad que mapea.
 * </p>
 * 
 * <p>
 * Esta clase proveé toda la información correspondiente al número de columnas
 * que posee la tabla, columnas y sus respectivos tipos de datos.
 * </p>
 * 
 * <p>
 * Un Data Mapper debe proveer métodos para transformar una entidad del modelo
 * en un registro de la base de datos y vicerversa. Para esto se tienen los 
 * métodos <i></i>.
 * </p>
 * 
 * @author hcarrizo
 */
public abstract class GenericDataMapper {

    /**
     * Tabla que administra el data mapper.
     */
    private opTable miTabla;
    
    /**
     * <p>
     * Este constructor crea la estructura general del data mapper. Obtiene los
     * metadatos de la tabla que mapea y deja inicializado el mapper para su
     * uso.
     * </p>
     * 
     * @param tableName nombre de la tabla que mapea el data mapper.
     * 
     * @throws ar.com.arkios.kfcon4d.exception.Open4DException
     */
    public GenericDataMapper(String tableName) throws Open4DException {
        // Obtengo el numero de la tabla que administra el data mapper para 
        // luego obtener los datos de su estructura.
        Connection4D miConeccion = Connection4D.getInstance();
        this.miTabla = new opTable(miConeccion.getTableNumber(tableName));

        // Obtengo los datos de la estructura completa de la tabla que 
        // administra el data mapper.
        opProcess miProcess = Connection4D.getInstance().getMyProcess();
        
        try {
            miProcess.GetTableProperties(miTabla);
        } catch (opException e) {
            throw new Open4DException("No se pudo obtener los metadatos de la tabla " + tableName, e);
        }
    }
    
    public abstract HashMap mappedAttributes();
    
    public abstract Class<?> getMappedClass();
    
    /**
     * 
     * 
     * @param registro
     * 
     * @return
     */
    public Object toModel(opDataArray registro) {
        try {
            Object o = getMappedClass().newInstance();
            
            for (opData data : registro.mDataArray) {
                
            }
            
            //BeanUtils.populate(o, arg1);
            
            return o;
        } catch (Exception e) {
            throw new Open4DException(e);
        }
    }
    
    /**
     * 
     * 
     * @param objeto
     * 
     * @return
     */
    public opDataArray toRegistro(Object objeto) {
        return null;
    }
    
    /**
     * <p>
     * Devuelve el número de la tabla que mapea el data mapper.
     * </p>
     * 
     * <p>
     * Este método esta pensado para ser utilizado con la librería de 4D la cual
     * realiza las consultas a la base de datos identificando las tablas a 
     * través de su número identificatorio.
     * </p>
     * 
     * @return tabla que mapea el data mapper
     */
    public short getTable() {
        return this.miTabla.mTableNumber;
    }
    
    /**
     * Crea un opData <i>(dato de registro de la base de datos)</i> conteniendo
     * el valor indicado para el parámetro.
     * 
     * @param field campo para el que se desea crear el opData.
     * @param value valor que se le quiere asignar al opData.
     * 
     * @return un opData conteniendo el valor indicado por parámetro.
     */
    protected opData createData(opField field, String value) {
        return new opData(field.mFieldType, value);
    }
    
    /**
     * @see GenericDAO#createData(ar.com.arkios.j4d.opField, java.lang.String) 
     */
    protected opData createData(opField field, Date value) {
        GregorianCalendar gc = new GregorianCalendar();
        gc.setTime(value);
        return new opData(opConstants.DATE, gc);
    }
    
    protected opData createData(opField field, boolean value) {
        return new opData(opConstants.BOOLEAN, value);
    }
    
    protected opData createData(opField field, double value) {
        return new opData(opConstants.REAL, value);
    }
    
    protected opData createData(opField field, int value) {
        return new opData(opConstants.LONGINTEGER, value);
    }
    
    protected opData createData(opField field, short value) {
        return new opData(opConstants.INTEGER, value);
    }
    
    protected opData createData(opField field, byte[] value) {
        return new opData(opConstants.BLOB, value);
    }
    
}
